{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Multi-agent supervisor\n",
    "\n",
    "We can to use an LLM to orchestrate the different agents.\n",
    "\n",
    "Below, we will create an agent group, with an agent supervisor to help delegate tasks.\n",
    "\n",
    "## Agentic Architecture\n",
    "\n",
    "![original image]()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "var userHomeDir = System.getProperty(\"user.home\");\n",
    "var localRespoUrl = \"file://\" + userHomeDir + \"/.m2/repository/\";\n",
    "var langchain4jVersion = \"1.0.0-beta2\";\n",
    "var langgraph4jVersion = \"1.5-SNAPSHOT\";"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash \n",
    "rm -rf \\{userHomeDir}/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/org/bsc/langgraph4j"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[0mRepository \u001b[1m\u001b[32mlocal\u001b[0m url: \u001b[1m\u001b[32mfile:///Users/bsorrentino/.m2/repository/\u001b[0m added.\n",
      "\u001b[0mAdding dependency \u001b[0m\u001b[1m\u001b[32morg.slf4j:slf4j-jdk14:2.0.9\n",
      "\u001b[0mAdding dependency \u001b[0m\u001b[1m\u001b[32morg.bsc.langgraph4j:langgraph4j-core:1.4-SNAPSHOT\n",
      "\u001b[0mAdding dependency \u001b[0m\u001b[1m\u001b[32morg.bsc.langgraph4j:langgraph4j-langchain4j:1.4-SNAPSHOT\n",
      "\u001b[0mAdding dependency \u001b[0m\u001b[1m\u001b[32mdev.langchain4j:langchain4j:1.0.0-beta2\n",
      "\u001b[0mAdding dependency \u001b[0m\u001b[1m\u001b[32mdev.langchain4j:langchain4j-ollama:1.0.0-beta2\n",
      "\u001b[0mAdding dependency \u001b[0m\u001b[1m\u001b[32mnet.sourceforge.plantuml:plantuml-mit:1.2024.8\n",
      "\u001b[0mSolving dependencies\n",
      "Resolved artifacts count: 16\n",
      "Add to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/org/slf4j/slf4j-jdk14/2.0.9/slf4j-jdk14-2.0.9.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/org/bsc/langgraph4j/langgraph4j-core/1.4-SNAPSHOT/langgraph4j-core-1.4-SNAPSHOT.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/org/bsc/async/async-generator/3.0.0/async-generator-3.0.0.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/org/bsc/langgraph4j/langgraph4j-langchain4j/1.4-SNAPSHOT/langgraph4j-langchain4j-1.4-SNAPSHOT.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/dev/langchain4j/langchain4j/1.0.0-beta2/langchain4j-1.0.0-beta2.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/dev/langchain4j/langchain4j-core/1.0.0-beta2/langchain4j-core-1.0.0-beta2.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/org/apache/opennlp/opennlp-tools/1.9.4/opennlp-tools-1.9.4.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/dev/langchain4j/langchain4j-ollama/1.0.0-beta2/langchain4j-ollama-1.0.0-beta2.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/dev/langchain4j/langchain4j-http-client-jdk/1.0.0-beta2/langchain4j-http-client-jdk-1.0.0-beta2.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/dev/langchain4j/langchain4j-http-client/1.0.0-beta2/langchain4j-http-client-1.0.0-beta2.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/com/fasterxml/jackson/core/jackson-databind/2.18.2/jackson-databind-2.18.2.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/com/fasterxml/jackson/core/jackson-annotations/2.18.2/jackson-annotations-2.18.2.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/com/fasterxml/jackson/core/jackson-core/2.18.2/jackson-core-2.18.2.jar\u001b[0m\n",
      "\u001b[0mAdd to classpath: \u001b[0m\u001b[32m/Users/bsorrentino/Library/Jupyter/kernels/rapaio-jupyter-kernel/mima_cache/net/sourceforge/plantuml/plantuml-mit/1.2024.8/plantuml-mit-1.2024.8.jar\u001b[0m\n",
      "\u001b[0m"
     ]
    }
   ],
   "source": [
    "%dependency /add-repo local \\{localRespoUrl} release|never snapshot|always\n",
    "// %dependency /list-repos\n",
    "%dependency /add org.slf4j:slf4j-jdk14:2.0.9\n",
    "%dependency /add org.bsc.langgraph4j:langgraph4j-core:\\{langgraph4jVersion}\n",
    "%dependency /add org.bsc.langgraph4j:langgraph4j-langchain4j:\\{langgraph4jVersion}\n",
    "%dependency /add dev.langchain4j:langchain4j:\\{langchain4jVersion}\n",
    "// %dependency /add dev.langchain4j:langchain4j-open-ai:\\{langchain4jVersion}\n",
    "%dependency /add dev.langchain4j:langchain4j-ollama:\\{langchain4jVersion}\n",
    "%dependency /add net.sourceforge.plantuml:plantuml-mit:1.2024.8\n",
    "\n",
    "%dependency /resolve"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**utility to render graph respresentation in PlantUML**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import net.sourceforge.plantuml.SourceStringReader;\n",
    "import net.sourceforge.plantuml.FileFormatOption;\n",
    "import net.sourceforge.plantuml.FileFormat;\n",
    "import org.bsc.langgraph4j.GraphRepresentation;\n",
    "\n",
    "void displayDiagram( GraphRepresentation representation ) throws IOException { \n",
    "    \n",
    "    var reader = new SourceStringReader(representation.getContent());\n",
    "\n",
    "    try(var imageOutStream = new java.io.ByteArrayOutputStream()) {\n",
    "\n",
    "        var description = reader.outputImage( imageOutStream, 0, new FileFormatOption(FileFormat.PNG));\n",
    "\n",
    "        var imageInStream = new java.io.ByteArrayInputStream(  imageOutStream.toByteArray() );\n",
    "\n",
    "        var image = javax.imageio.ImageIO.read( imageInStream );\n",
    "\n",
    "        display(  image );\n",
    "\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Initialize Log**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "var lm = java.util.logging.LogManager.getLogManager();\n",
    "lm.checkAccess(); \n",
    "try( var file = new java.io.FileInputStream(\"./logging.properties\")) {\n",
    "    lm.readConfiguration( file );\n",
    "}\n",
    "var log = org.slf4j.LoggerFactory.getLogger(\"multi-agent-supervisor\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Graph State**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "import org.bsc.langgraph4j.prebuilt.MessagesState;\n",
    "import dev.langchain4j.data.message.ChatMessage;\n",
    "import java.util.Optional;\n",
    "\n",
    "class State extends MessagesState<ChatMessage> {\n",
    "\n",
    "    public Optional<String> next() {\n",
    "        return this.value(\"next\");\n",
    "    }\n",
    "\n",
    "    public State(Map<String, Object> initData) {\n",
    "        super( initData  );\n",
    "    }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Create Serializer**\n",
    "\n",
    "This is important for supporting persistent state across graph execution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "import org.bsc.langgraph4j.langchain4j.serializer.std.ChatMesssageSerializer;\n",
    "import org.bsc.langgraph4j.langchain4j.serializer.std.ToolExecutionRequestSerializer;\n",
    "import org.bsc.langgraph4j.serializer.std.ObjectStreamStateSerializer;\n",
    "import dev.langchain4j.agent.tool.ToolExecutionRequest;\n",
    "\n",
    "class StateSerializer extends ObjectStreamStateSerializer<State> {\n",
    "\n",
    "    public StateSerializer() {\n",
    "        super(State::new);\n",
    "\n",
    "        mapper().register(ToolExecutionRequest.class, new ToolExecutionRequestSerializer());\n",
    "        mapper().register(ChatMessage.class, new ChatMesssageSerializer());\n",
    "    }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Supervisor Agent\n",
    "\n",
    "⚠️ Since this is just a POC the tool is mocked ⚠️"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "import dev.langchain4j.agent.tool.P;\n",
    "import dev.langchain4j.agent.tool.Tool;\n",
    "import dev.langchain4j.model.output.structured.Description;\n",
    "import dev.langchain4j.service.V;\n",
    "import dev.langchain4j.service.SystemMessage;\n",
    "import dev.langchain4j.service.AiServices;\n",
    "import dev.langchain4j.data.message.AiMessage;\n",
    "import dev.langchain4j.data.message.UserMessage;\n",
    "import dev.langchain4j.model.chat.ChatLanguageModel;\n",
    "import org.bsc.langgraph4j.action.NodeAction;\n",
    "import java.util.concurrent.CompletableFuture;\n",
    "import static java.lang.String.format;\n",
    "\n",
    "\n",
    "class SupervisorAgent implements NodeAction<State> {\n",
    "\n",
    "    static class Router {\n",
    "        @Description(\"Worker to route to next. If no workers needed, route to FINISH.\")\n",
    "        String next;\n",
    "\n",
    "        @Override\n",
    "        public String toString() {\n",
    "            return format( \"Router[next: %s]\",next);\n",
    "        }\n",
    "    }\n",
    "\n",
    "    interface Service {\n",
    "        @SystemMessage( \"\"\"\n",
    "                You are a supervisor tasked with managing a conversation between the following workers: {{members}}.\n",
    "                Given the following user request, respond with the worker to act next.\n",
    "                Each worker will perform a task and respond with their results and status.\n",
    "                When finished, respond with FINISH.\n",
    "                \"\"\")\n",
    "        Router evaluate(@V(\"members\") String members, @dev.langchain4j.service.UserMessage String userMessage);\n",
    "    }\n",
    "\n",
    "    final Service service;\n",
    "    public final String[] members = {\"researcher\", \"coder\" };\n",
    "\n",
    "    public SupervisorAgent(ChatLanguageModel model ) {\n",
    "\n",
    "        service = AiServices.create( Service.class, model );\n",
    "    }\n",
    "\n",
    "    @Override\n",
    "    public Map<String, Object> apply(State state) throws Exception {    \n",
    "        \n",
    "        var message = state.lastMessage().orElseThrow();\n",
    "\n",
    "        var text = switch( message.type() ) {\n",
    "            case USER -> ((UserMessage)message).singleText();\n",
    "            case AI -> ((AiMessage)message).text();\n",
    "            default -> throw new IllegalStateException(\"unexpected message type: \" + message.type() );\n",
    "        };\n",
    "\n",
    "        var m = String.join(\",\", members);\n",
    "        \n",
    "        var result = service.evaluate( m, text );\n",
    "        \n",
    "        return Map.of( \"next\", result.next );\n",
    "    }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Researcher Agent\n",
    "\n",
    "⚠️ Since this is just a POC the tool is mocked ⚠️"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ResearchAgent implements NodeAction<State> {\n",
    "    static class Tools {\n",
    "\n",
    "        @Tool(\"\"\"\n",
    "        Use this to perform a research over internet\n",
    "        \"\"\")\n",
    "        String search(@P(\"internet query\") String query) {\n",
    "            log.info( \"search query: '{}'\", query );\n",
    "            return \"\"\"\n",
    "            the games will be in Italy at Cortina '2026\n",
    "            \"\"\";\n",
    "        }\n",
    "    }\n",
    "\n",
    "    interface Service {\n",
    "        String search(@dev.langchain4j.service.UserMessage  String query);\n",
    "    }\n",
    "\n",
    "    final Service service;\n",
    "\n",
    "    public ResearchAgent( ChatLanguageModel model ) {\n",
    "        service = AiServices.builder( Service.class )\n",
    "                        .chatLanguageModel(model)\n",
    "                        .tools( new Tools() )\n",
    "                        .build();\n",
    "    }\n",
    "    @Override\n",
    "    public Map<String, Object> apply(State state) throws Exception {\n",
    "        var message = state.lastMessage().orElseThrow();\n",
    "        var text = switch( message.type() ) {\n",
    "            case USER -> ((UserMessage)message).singleText();\n",
    "            case AI -> ((AiMessage)message).text();\n",
    "            default -> throw new IllegalStateException(\"unexpected message type: \" + message.type() );\n",
    "        };\n",
    "        var result = service.search( text );\n",
    "        return Map.of( \"messages\", AiMessage.from(result) );\n",
    "\n",
    "    }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Coder Agent\n",
    "\n",
    "⚠️ Since this is just a POC the tool is mocked ⚠️"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CoderAgent implements NodeAction<State> {\n",
    "    static class Tools {\n",
    "\n",
    "        @Tool(\"\"\"\n",
    "            Use this to execute java code and do math. If you want to see the output of a value,\n",
    "            you should print it out with `System.out.println(...);`. This is visible to the user.\"\"\")\n",
    "        String search(@P(\"coder request\") String request) {\n",
    "            log.info( \"CoderTool request: '{}'\", request );\n",
    "            return \"\"\"\n",
    "            2\n",
    "            \"\"\";\n",
    "        }\n",
    "    }\n",
    "\n",
    "    interface Service {\n",
    "        String evaluate(@dev.langchain4j.service.UserMessage String code);\n",
    "    }\n",
    "\n",
    "    final Service service;\n",
    "\n",
    "    public CoderAgent( ChatLanguageModel model ) throws Exception {\n",
    "        service = AiServices.builder( Service.class )\n",
    "                .chatLanguageModel(model)\n",
    "                .tools( new Tools() )\n",
    "                .build();\n",
    "    }\n",
    "    @Override\n",
    "    public Map<String, Object> apply(State state) {\n",
    "        var message = state.lastMessage().orElseThrow();\n",
    "        var text = switch( message.type() ) {\n",
    "            case USER -> ((UserMessage)message).singleText();\n",
    "            case AI -> ((AiMessage)message).text();\n",
    "            default -> throw new IllegalStateException(\"unexpected message type: \" + message.type() );\n",
    "            };\n",
    "        var result = service.evaluate( text );\n",
    "        return Map.of( \"messages\", AiMessage.from(result) );\n",
    "\n",
    "    }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Initialize OLLAMA Models**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "import dev.langchain4j.model.ollama.OllamaChatModel;\n",
    "\n",
    "final ChatLanguageModel model = OllamaChatModel.builder()\n",
    "    .baseUrl( \"http://localhost:11434\" )\n",
    "    .temperature(0.0)\n",
    "    .logRequests(true)\n",
    "    .logResponses(true)\n",
    "    .format( \"json\" )\n",
    "    .modelName(\"deepseek-r1:14b\")\n",
    "    .build();\n",
    "\n",
    "final ChatLanguageModel modelWithTool = OllamaChatModel.builder()\n",
    "    .baseUrl( \"http://localhost:11434\" )\n",
    "    .temperature(0.0)\n",
    "    .logRequests(true)\n",
    "    .logResponses(true)\n",
    "    .modelName(\"llama3.1:latest\")\n",
    "    .build();\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define Graph\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "import org.bsc.langgraph4j.StateGraph;\n",
    "import static org.bsc.langgraph4j.StateGraph.END;\n",
    "import static org.bsc.langgraph4j.StateGraph.START;\n",
    "import static org.bsc.langgraph4j.action.AsyncEdgeAction.edge_async;\n",
    "import static org.bsc.langgraph4j.action.AsyncNodeAction.node_async;\n",
    "\n",
    "\n",
    "var supervisor = new SupervisorAgent(model);\n",
    "var coder = new CoderAgent(modelWithTool);\n",
    "var researcher = new ResearchAgent(modelWithTool);\n",
    "\n",
    "var workflow = new StateGraph<>( State.SCHEMA, new StateSerializer() )\n",
    ".addNode( \"supervisor\", node_async(supervisor)) \n",
    ".addNode( \"coder\", node_async(coder) )\n",
    ".addNode( \"researcher\",node_async(researcher) )\n",
    ".addEdge( START, \"supervisor\")\n",
    ".addConditionalEdges( \"supervisor\",\n",
    "        edge_async( state ->\n",
    "            state.next().orElseThrow()\n",
    "        ), Map.of(\n",
    "                \"FINISH\", END,\n",
    "                \"coder\", \"coder\",\n",
    "                \"researcher\", \"researcher\"\n",
    "        ))\n",
    ".addEdge( \"coder\", \"supervisor\")\n",
    ".addEdge( \"researcher\", \"supervisor\")\n",
    ";\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Display StateGraph**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "var representation = workflow.getGraph( GraphRepresentation.Type.PLANTUML, \"sub graph\", false );\n",
    "\n",
    "displayDiagram( representation );"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Run Graph (Supervisor -> coder)** "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "START \n",
      "NodeOutput{node=__START__, state={messages=[UserMessage { name = null contents = [TextContent { text = \"what are the result of 1 + 1 ?\" }] }]}} \n",
      "CoderTool request: 'System.out.println(1+1);' \n",
      "NodeOutput{node=supervisor, state={next=coder, messages=[UserMessage { name = null contents = [TextContent { text = \"what are the result of 1 + 1 ?\" }] }]}} \n",
      "NodeOutput{node=coder, state={next=coder, messages=[UserMessage { name = null contents = [TextContent { text = \"what are the result of 1 + 1 ?\" }] }, AiMessage { text = \"The result of 1 + 1 is 2.\" toolExecutionRequests = null }]}} \n",
      "NodeOutput{node=supervisor, state={next=FINISH, messages=[UserMessage { name = null contents = [TextContent { text = \"what are the result of 1 + 1 ?\" }] }, AiMessage { text = \"The result of 1 + 1 is 2.\" toolExecutionRequests = null }]}} \n",
      "NodeOutput{node=__END__, state={next=FINISH, messages=[UserMessage { name = null contents = [TextContent { text = \"what are the result of 1 + 1 ?\" }] }, AiMessage { text = \"The result of 1 + 1 is 2.\" toolExecutionRequests = null }]}} \n"
     ]
    }
   ],
   "source": [
    "var graph = workflow.compile();\n",
    "\n",
    "for( var event : graph.stream( Map.of( \"messages\", UserMessage.from(\"what are the result of 1 + 1 ?\"))) ) {\n",
    "\n",
    "    log.info( \"{}\", event );\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Run Graph (Supervisor -> researcher)** "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "START \n",
      "NodeOutput{node=__START__, state={messages=[UserMessage { name = null contents = [TextContent { text = \"where are next winter olympic games ?\" }] }]}} \n",
      "search query: 'next winter olympic games location' \n",
      "NodeOutput{node=supervisor, state={next=researcher, messages=[UserMessage { name = null contents = [TextContent { text = \"where are next winter olympic games ?\" }] }]}} \n",
      "NodeOutput{node=researcher, state={next=researcher, messages=[UserMessage { name = null contents = [TextContent { text = \"where are next winter olympic games ?\" }] }, AiMessage { text = \"The next Winter Olympic Games will take place in Cortina d'Ampezzo, Italy in 2026.\" toolExecutionRequests = null }]}} \n",
      "NodeOutput{node=supervisor, state={next=FINISH, messages=[UserMessage { name = null contents = [TextContent { text = \"where are next winter olympic games ?\" }] }, AiMessage { text = \"The next Winter Olympic Games will take place in Cortina d'Ampezzo, Italy in 2026.\" toolExecutionRequests = null }]}} \n",
      "NodeOutput{node=__END__, state={next=FINISH, messages=[UserMessage { name = null contents = [TextContent { text = \"where are next winter olympic games ?\" }] }, AiMessage { text = \"The next Winter Olympic Games will take place in Cortina d'Ampezzo, Italy in 2026.\" toolExecutionRequests = null }]}} \n"
     ]
    }
   ],
   "source": [
    "var graph = workflow.compile();\n",
    "\n",
    "for( var event : graph.stream( Map.of( \"messages\", UserMessage.from(\"where are next winter olympic games ?\"))) ) {\n",
    "\n",
    "    log.info( \"{}\", event );\n",
    "}\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Java (rjk 2.2.0)",
   "language": "java",
   "name": "rapaio-jupyter-kernel"
  },
  "language_info": {
   "codemirror_mode": "java",
   "file_extension": ".jshell",
   "mimetype": "text/x-java-source",
   "name": "java",
   "nbconvert_exporter": "script",
   "pygments_lexer": "java",
   "version": "17.0.2+8-86"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
